﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StockholmsStad.Data.Interfaces;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using StockholmsStad.Utilities.Exceptions;

namespace StockholmsStad.Data.Test
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class TestUnitMinister : TestUnitBase
    {
        // before running each test 
        [TestInitialize()]
        public void MyTestInitialize()
        {
            BeginTransaction();
        }

        // after each test has run
        [TestCleanup()]
        public void MyTestCleanup()
        {
            EndTransaction();
        }

        [TestMethod]
        public void GetNonExistingMinisterCode()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            int? ministerId = ministerAgent.GetMinisterIdByMinisterCode(context, "Gurka43434343443");
            Assert.AreEqual(null, ministerId);
        }

        [TestMethod]
        public void GetNonExistingMinisterCode2()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister minister = ministerAgent.GetMinisterByMinisterCode(context, "Gurka43434343443");
            Assert.AreEqual(null, minister);
        }

        [TestMethod]
        public void GetExistingMinisterCode()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister minister = TestEntity.GetNewMinister();
            int id = ministerAgent.AddMinister(context, minister);

            int? ministerId = ministerAgent.GetMinisterIdByMinisterCode(context, minister.MinisterCode);
            Assert.AreEqual(id, ministerId.Value);
        }


        [TestMethod]
        public void AddAndRetrieveMinisterByMinisterId()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinister();
            int id = ministerAgent.AddMinister(context, expected);
            expected.MinisterId = id;
            Assert.AreNotEqual(0, id);

            Minister actual = ministerAgent.GetMinister(context, expected.MinisterId.Value);
            Assert.AreEqual(id, actual.MinisterId);

            AreMinisterEqual(expected, actual);
        }

        [TestMethod]
        public void AddAndRetrieveMinisterByMinisterCode()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinister();
            int id = ministerAgent.AddMinister(context, expected);
            expected.MinisterId = id;
            Assert.AreNotEqual(0, id);

            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, expected.MinisterCode);
            Assert.AreEqual(id, actual.MinisterId);
        }

        [TestMethod]
        public void AddAndRetrieveMinisterWithNewLanguage()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinisterWithNewLanguage();
            int id = ministerAgent.AddMinister(context, expected);
            Assert.AreNotEqual(0, id);
            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, expected.MinisterCode);
            Assert.AreEqual(id, actual.MinisterId);
            AreMinisterEqual(expected, actual);
        }

        [TestMethod]
        public void UpdateMinister()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister old = TestEntity.GetNewMinister();
            ministerAgent.AddMinister(context, old);
            Minister update = ministerAgent.GetMinisterByMinisterCode(context, old.MinisterCode);
            update.Address.City = "ciup";
            update.Address.CoAddress = "coup";
            update.Address.Country = "coup";
            update.Address.PostCode = "pup";
            update.Address.StreetAddress = "sup";
            update.AssignmentEndDate = DateTime.Now;
            update.AssignmentStartDate = DateTime.Now;
            update.MinisterCode = "bup";
            update.CellPhoneNumber = "cpup";
            update.Email = "eup";
            update.FirstNames = "fup";
            update.LastName = "lup";
            update.Note = "nup";
            update.PhoneNumber = "pnup";
            update.SpecialAssignment = true;
            ministerAgent.UpdateMinister(context, update.MinisterId.Value, update);
            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, update.MinisterCode);
            AreMinisterEqual(update, actual);
        }

        private static void AreMinisterEqual(Minister expected, Minister actual)
        {
            Assert.AreEqual(expected.MinisterCode, actual.MinisterCode);
            Assert.AreEqual(expected.FirstNames, actual.FirstNames);
            Assert.AreEqual(expected.LastName, actual.LastName);
            Assert.AreEqual(expected.CellPhoneNumber, actual.CellPhoneNumber);
            Assert.AreEqual(expected.PhoneNumber, actual.PhoneNumber);
            Assert.AreEqual(expected.Note, actual.Note);
            Assert.AreEqual(expected.Email, actual.Email);
            Assert.AreEqual(expected.AssignmentStartDate.ToString("yyyy-MM-dd"), actual.AssignmentStartDate.ToString("yyyy-MM-dd"));
            Assert.AreEqual(expected.AssignmentEndDate.ToString("yyyy-MM-dd"), actual.AssignmentEndDate.ToString("yyyy-MM-dd"));

            AreAddressEqual(expected.Address, actual.Address);
            AreLanguagesEqual(expected.Languages, actual.Languages);
        }

        [TestMethod]
        [ExpectedException(typeof (StockholmStadSystemException))]
        public void DoubleMinisterBookingCodeTest()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinister();
            ministerAgent.AddMinister(context, expected);
            ministerAgent.AddMinister(context, expected);
        }

        [TestMethod]
        public void GetMinistersTest()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            List<Minister> oldMinisters = ministerAgent.GetMinisters(context);
            Minister expected = TestEntity.GetNewMinister();
            int ministerId = ministerAgent.AddMinister(context, expected);
            List<Minister> newMinisters = ministerAgent.GetMinisters(context);
            Assert.AreEqual(1, newMinisters.Count - oldMinisters.Count);
            Minister minister = newMinisters.Where(m => m.MinisterId == ministerId).First();
            AreMinisterEqual(expected, minister);
        }

        [TestMethod]
        public void RemoveMinisterTest()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinister();
            ministerAgent.AddMinister(context, expected);
            Minister minister = ministerAgent.GetMinisterByMinisterCode(context, expected.MinisterCode);
            Assert.IsNotNull(minister);
            ministerAgent.RemoveMinister(context, minister.MinisterId.Value);
            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, expected.MinisterCode);
            Assert.IsNull(actual);
        }

        [TestMethod]
        public void AddAndRetrieveMinistersGivenADate()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinister();
            int id = ministerAgent.AddMinister(context, expected);
            Assert.AreNotEqual(0, id);
            var ministers = ministerAgent.GetMinisters(context, expected.AssignmentStartDate.AddDays(1));

            Assert.AreNotEqual(0, ministers.Count, "Förväntat antal förrättare felaktigt");
            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, expected.MinisterCode);
            Assert.AreEqual(id, actual.MinisterId);
            AreMinisterEqual(expected, actual);
        }

        [TestMethod]
        public void AddMinisterAndRetrieveByBookingId()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expectedMinister = TestEntity.GetNewMinister();
            int ministerId = ministerAgent.AddMinister(context, expectedMinister);
            Assert.AreNotEqual(0, ministerId);
            expectedMinister.MinisterId = ministerId;
            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, expectedMinister.MinisterCode);
            Assert.AreEqual(ministerId, actual.MinisterId);
            AreMinisterEqual(expectedMinister, actual);

            // Add booking with minister
            IBookingAgent bookingAgent = AgentFactory.GetBookingAgent();
            MinisterTimeBlock timeBlock = TestEntity.GetNewTimeBlock();
            timeBlock.Minister = expectedMinister;
            IMaintenanceAgent maintenanceAgent = AgentFactory.GetMaintenanceAgent();
            var timeblockId = maintenanceAgent.AddTimeBlock(context, timeBlock);
            Assert.AreNotEqual(0, timeblockId, "Inget tidsblock skapades");

            var expectedBooking = TestEntity.GetOrdinaryBooking();
            expectedBooking.DateTime = timeBlock;
            expectedBooking.DateTime.TimeBlockId = timeblockId;

            // Add ticket
            var couple = TestEntity.GetNewCoupleWithoutContact();
            var ceremony = TestEntity.GetNewCeremony();

            var languageAgent = AgentFactory.GetLanguageAgent();
            var langId = languageAgent.AddLanguage(context, ceremony.CeremonyLanguage.Name);
            Assert.AreNotEqual(0, langId, "Inget språk skapades");

            var ceremonyAgent = AgentFactory.GetCeremonyAgent();
            var cerLangId = languageAgent.AddCeremonyLanguage(context, ceremony.CeremonyLanguage.Name);
            Assert.AreNotEqual(0, cerLangId, "Inget ceremonispråk skapades");

            var certLangId = languageAgent.AddCertificateLanguage(context, ceremony.CertificateLanguage.Name);
            Assert.AreNotEqual(0, certLangId, "Inget certifikatspråk skapades");

            var ticketAgent = AgentFactory.GetTicketAgent();
            var ticketId = ticketAgent.CreateTicket(context, couple, ceremony);

            Assert.AreNotEqual(0, ticketId, "Inget ärende skapades");

            bookingAgent.AddBooking(context, ticketId, expectedBooking, ministerId);

            var retrievedBooking = bookingAgent.GetBooking(context, ticketId);
            Assert.IsNotNull(retrievedBooking, "Finns ingen sådan bokning");

            var retrievedMinister = ministerAgent.GetMinisterByTicket(context, ticketId);
            Assert.IsNotNull(retrievedMinister, "Finns ingen sådan förrättare");

            Assert.AreEqual(expectedMinister.FirstNames, retrievedMinister.FirstNames, "Förrättaren förnamn matchar inte");
            Assert.AreEqual(expectedMinister.LastName, retrievedMinister.LastName, "Förrättaren förnamn matchar inte");
            Assert.AreEqual(expectedMinister.MinisterId, retrievedMinister.MinisterId, "Förrättaren förnamn matchar inte");
        }

        [TestMethod]
        public void AddMinisterAndTimeBlocksAndRetrieve()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinister();
            int id = ministerAgent.AddMinister(context, expected);
            expected.MinisterId = id;
            Assert.AreNotEqual(0, id);

            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, expected.MinisterCode);
            Assert.AreEqual(id, actual.MinisterId);
            AreMinisterEqual(expected, actual);

            DateTime date = DateTime.Now;
            MinisterTimeBlock t = new MinisterTimeBlock{
                StartDateTime = date, 
                TimeBlockStatus = TimeBlockStatus.Bookable, 
                Minister = expected, 
                Interval = 5, 
                TempMinister = null};

            IMaintenanceAgent maintenanceAgent = AgentFactory.GetMaintenanceAgent();
            maintenanceAgent.AddTimeBlock(context, t);

            var times = ministerAgent.GetTimeBlocksForMinister(context, expected.MinisterId.Value);
            Assert.IsNotNull(times, "Inga tider lades till förrättaren");
            Assert.AreEqual(1, times.Count, "Antal tider för förrättaren stämmer inte");

            AreTimeBlockEqual(t, times[0]);
        }

        [TestMethod]
        public void AddMinisterAndTempMinisterAndTimeBlocksAndRetrieve()
        {
            IMinisterAgent ministerAgent = AgentFactory.GetMinisterAgent();
            Minister expected = TestEntity.GetNewMinister();
            int id = ministerAgent.AddMinister(context, expected);
            expected.MinisterId = id;
            Assert.AreNotEqual(0, id);

            Minister actual = ministerAgent.GetMinisterByMinisterCode(context, expected.MinisterCode);
            Assert.AreEqual(id, actual.MinisterId);
            AreMinisterEqual(expected, actual);

            DateTime date = DateTime.Now;
            var t1 = new MinisterTimeBlock
            {
                StartDateTime = date,
                TimeBlockStatus = TimeBlockStatus.Bookable,
                Minister = actual,
                Interval = 5,
                TempMinister = null
            };

            var date2 = date.AddDays(1);
            var t2 = new MinisterTimeBlock
            {
                StartDateTime = date2,
                TimeBlockStatus = TimeBlockStatus.Bookable,
                Minister = null,
                Interval = 5,
                TempMinister = actual,
            };

            var maintenanceAgent = AgentFactory.GetMaintenanceAgent();
            maintenanceAgent.AddTimeBlock(context, t1);
            maintenanceAgent.AddTimeBlock(context, t2);

            var times = ministerAgent.GetTimeBlocksForMinister(context, actual.MinisterId.Value);
            Assert.IsNotNull(times, "Inga tider lades till förrättaren");
            Assert.AreEqual(2, times.Count, "Antal tider för förrättaren stämmer inte");

            Assert.AreEqual(null, times[0].TempMinister, "Tidsblocket har en vik förrättare");
            Assert.AreEqual(null, times[1].Minister, "Tidsblocket har en förrättare");

            AreTimeBlockEqual(t1, times[0]);
            AreTimeBlockEqual(t2, times[1]);
        }

        private static void AreTimeBlockEqual(MinisterTimeBlock expected, MinisterTimeBlock actual)
        {
            Assert.AreEqual(expected.StartDateTime.ToString("yyyy-MM-dd"), actual.StartDateTime.ToString("yyyy-MM-dd"),
                string.Format("Intervallet stämmer inte. Förväntat: {0}, Erhållet: {1}",
                expected.StartDateTime.ToString("yyyy-MM-dd"), actual.StartDateTime.ToString("yyyy-MM-dd")));

            Assert.AreEqual(expected.StartDateTime.ToString("HH:mm"), actual.StartTime.ToString("HH:mm"),
                string.Format("Starttiden stämmer inte. Förväntat: {0}, Erhållet: {1}",
                expected.StartDateTime.ToString("HH:mm"), actual.StartTime.ToString("HH:mm")));

            Assert.AreEqual(expected.StartDateTime.ToString("HH:mm"), actual.StartTime.ToString("HH:mm"),
                string.Format("Sluttiden stämmer inte. Förväntat: {0}, Erhållet: {1}",
                expected.StartTime.ToString("HH:mm"), actual.StartTime.ToString("HH:mm")));
        }

        private static void AreLanguagesEqual(IList<string> expected, IList<string> actual)
        {
            Assert.AreEqual(expected.Count(), actual.Count, "Antal språk stämmer inte överens");

            for (int i = 0; i < expected.Count - 1; i++)
            {
                Assert.AreEqual(expected[i], actual[i], "språken stämmer inte överens");
            }
        }

        private static void AreAddressEqual(Address expectedAddress, Address retrievedAddress)
        {
            Assert.AreEqual(expectedAddress.City, retrievedAddress.City,
                            string.Format("Retrieved city ({0}) not the same as expected city ({1})",
                                          expectedAddress.City, retrievedAddress.City));
            Assert.AreEqual(expectedAddress.CoAddress, retrievedAddress.CoAddress,
                            string.Format("Retrieved CoAddress ({0}) not the same as expected CoAddress ({1})",
                                          expectedAddress.City, retrievedAddress.City));
            Assert.AreEqual(expectedAddress.Country, retrievedAddress.Country,
                            string.Format("Retrieved Country ({0}) not the same as expected Country ({1})",
                                          expectedAddress.City, retrievedAddress.City));
            Assert.AreEqual(expectedAddress.PostCode, retrievedAddress.PostCode,
                            string.Format("Retrieved PostCode ({0}) not the same as expected PostCode ({1})",
                                          expectedAddress.City, retrievedAddress.City));
            Assert.AreEqual(expectedAddress.StreetAddress, retrievedAddress.StreetAddress,
                            string.Format("Retrieved StreetAddress ({0}) not the same as expected StreetAddress ({1})",
                                          expectedAddress.City, retrievedAddress.City));
        }

        private void AreLanguagesEqual(ValueIdPair expected, ValueIdPair actual)
        {
            Assert.AreEqual(expected.Id, actual.Id);
            Assert.AreEqual(expected.Name, actual.Name);
        }
    }
}